package simplescenegraph;

import simplescenegraph.math.Matrix3f;
import simplescenegraph.math.Transform;
import simplescenegraph.math.Transformable;
import simplescenegraph.math.Vector3f;

public abstract class Node implements Transformable {

	protected Transform worldTransform = new Transform();
	
	protected Transform localTransform = new Transform();
	
	protected GroupNode parent = null;
	
	protected Object userData = null;
	
	protected boolean forceCulling = false;
	
	protected boolean updated = false;
	
	public void setForceCulling(boolean forceCulling) {
		this.forceCulling = forceCulling;
	}
	
	public void updateGeometricState(float time, boolean initiator) {
		if (updated) {
			updateWorldTransform(time);
			updateWorldBound();
			if (initiator)
				propagateBoundUpdateToRoot();
			
			updated = false;
		}
	}
	
	protected void updateWorldTransform(float time) {
		worldTransform = new Transform(localTransform);
		if (parent != null) {
			parent.worldTransform.apply(worldTransform);
		}
	}
	
	protected void propagateBoundUpdateToRoot() {
		if (parent != null) {
			parent.updateWorldBound();
			parent.propagateBoundUpdateToRoot();
		}
	}
	
	protected abstract void updateWorldBound();
	
	public boolean onDraw(Renderer renderer) {
		if (forceCulling) {
			return false;
		}
		
		if (!renderer.cull(getWorldBound())) {
			draw(renderer);
		}
		
		return true;
	}
	
	public void draw(Renderer renderer) {
		renderer.draw(this);
	}
	
	public abstract BoundingVolume getWorldBound();

	public Matrix3f getOrientation() {
		return worldTransform.getRotate();
	}

	public Vector3f getPosition() {
		return worldTransform.getTranslate();
	}

	public Vector3f getScale() {
		Matrix3f scale = worldTransform.getScale();
		return new Vector3f(scale.m[0], scale.m[4], scale.m[8]);
	}

	public void rotateAxis(float a0, Vector3f a1) {
		localTransform.rotateAxis(a0, a1);
		updated = true;
	}

	public void rotateX(float a0) {
		localTransform.rotateX(a0);
		updated = true;
	}

	public void rotateY(float a0) {
		localTransform.rotateY(a0);
		updated = true;
	}

	public void rotateZ(float a0) {
		localTransform.rotateZ(a0);
		updated = true;
	}
	
	public void scale(float f0, float f1, float f2) {
		localTransform.scale(f0, f1, f2);
		updated = true;
	}

	public void translate(float f0, float f1, float f2) {
		localTransform.translate(f0, f1, f2);
		updated = true;
	}

	public GroupNode getParent() {
		return parent;
	}

	public void setParent(GroupNode parent) {
		this.parent = parent;
	}

	public Object getUserData() {
		return userData;
	}

	public void setUserData(Object userData) {
		this.userData = userData;
	}

	public Transform getWorldTransform() {
		return worldTransform;
	}

	public Transform getLocalTransform() {
		return localTransform;
	}

	@Override
	public void setPosition(Vector3f position) {
		localTransform.setTranslate(position);
		updated = true;
	}
	
}
 
